Completed
Push — master ( 2f8415...e6e0b0 )
by Rain
02:22
created

Knoin.js ➔ isPopupVisible   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
c 2
b 0
f 0
nc 4
nop 1
dl 0
loc 5
rs 9.4285
1
2
import _ from '_';
3
import $ from '$';
4
import ko from 'ko';
5
import hasher from 'hasher';
6
import crossroads from 'crossroads';
7
8
import {runHook} from 'Common/Plugins';
9
import {$html, aViewModels as VIEW_MODELS, popupVisibilityNames} from 'Common/Globals';
10
11
import {
12
	isArray, isUnd, pString, log, isFunc,
13
	createCommandLegacy, delegateRun, isNonEmptyArray
14
} from 'Common/Utils';
15
16
let
17
	currentScreen = null,
18
	defaultScreenName = '';
19
20
const SCREENS = {};
21
22
export const ViewType = {
23
	Popup: 'Popups',
24
	Left: 'Left',
25
	Right: 'Right',
26
	Center: 'Center'
27
};
28
29
/**
30
 * @returns {void}
31
 */
32
export function hideLoading()
33
{
34
	$('#rl-content').addClass('rl-content-show');
35
	$('#rl-loading').hide().remove();
36
}
37
38
/**
39
 * @param {Function} fExecute
40
 * @param {(Function|boolean|null)=} fCanExecute = true
41
 * @returns {Function}
42
 */
43
export function createCommand(fExecute, fCanExecute = true)
44
{
45
	return createCommandLegacy(null, fExecute, fCanExecute);
46
}
47
48
/**
49
 * @param {Function} SettingsViewModelClass
50
 * @param {string} template
51
 * @param {string} labelName
52
 * @param {string} route
53
 * @param {boolean=} isDefault = false
54
 * @returns {void}
55
 */
56
export function addSettingsViewModel(SettingsViewModelClass, template, labelName, route, isDefault = false)
57
{
58
	SettingsViewModelClass.__rlSettingsData = {
59
		Label: labelName,
60
		Template: template,
61
		Route: route,
62
		IsDefault: !!isDefault
63
	};
64
65
	VIEW_MODELS.settings.push(SettingsViewModelClass);
66
}
67
68
/**
69
 * @param {Function} SettingsViewModelClass
70
 * @returns {void}
71
 */
72
export function removeSettingsViewModel(SettingsViewModelClass)
73
{
74
	VIEW_MODELS['settings-removed'].push(SettingsViewModelClass);
75
}
76
77
/**
78
 * @param {Function} SettingsViewModelClass
79
 * @returns {void}
80
 */
81
export function disableSettingsViewModel(SettingsViewModelClass)
82
{
83
	VIEW_MODELS['settings-disabled'].push(SettingsViewModelClass);
84
}
85
86
/**
87
 * @returns {void}
88
 */
89
export function routeOff()
90
{
91
	hasher.changed.active = false;
92
}
93
94
/**
95
 * @returns {void}
96
 */
97
export function routeOn()
98
{
99
	hasher.changed.active = true;
100
}
101
102
/**
103
 * @param {string} screenName
104
 * @returns {?Object}
105
 */
106
export function screen(screenName)
107
{
108
	return ('' !== screenName && !isUnd(SCREENS[screenName])) ? SCREENS[screenName] : null;
109
}
110
111
/**
112
 * @param {Function} ViewModelClassToShow
0 ignored issues
show
Documentation introduced by
The parameter ViewModelClassToShow does not exist. Did you maybe forget to remove this comment?
Loading history...
113
 * @returns {Function|null}
114
 */
115
export function getScreenPopup(PopuViewModelClass)
116
{
117
	let result = null;
118
	if (PopuViewModelClass)
119
	{
120
		result = PopuViewModelClass;
121
		if (PopuViewModelClass.default)
122
		{
123
			result = PopuViewModelClass.default;
124
		}
125
	}
126
127
	return result;
128
}
129
130
/**
131
 * @param {Function} ViewModelClassToHide
132
 * @returns {void}
133
 */
134
export function hideScreenPopup(ViewModelClassToHide)
135
{
136
	const ModalView = getScreenPopup(ViewModelClassToHide);
137
	if (ModalView && ModalView.__vm && ModalView.__dom)
138
	{
139
		ModalView.__vm.modalVisibility(false);
140
	}
141
}
142
143
/**
144
 * @param {string} hookName
145
 * @param {Function} ViewModelClass
146
 * @param {mixed=} params = null
147
 */
148
export function vmRunHook(hookName, ViewModelClass, params = null)
149
{
150
	_.each(ViewModelClass.__names, (name) => {
151
		runHook(hookName, [name, ViewModelClass.__vm, params]);
152
	});
153
}
154
155
/**
156
 * @param {Function} ViewModelClass
157
 * @param {Object=} vmScreen
158
 * @returns {*}
159
 */
160
export function buildViewModel(ViewModelClass, vmScreen)
161
{
162
	if (ViewModelClass && !ViewModelClass.__builded)
163
	{
164
		let vmDom = null;
0 ignored issues
show
Unused Code introduced by
The assignment to vmDom seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
165
		const
166
			vm = new ViewModelClass(vmScreen),
167
			position = ViewModelClass.__type || '',
168
			vmPlace = position ? $('#rl-content #rl-' + position.toLowerCase()) : null;
169
170
		ViewModelClass.__builded = true;
171
		ViewModelClass.__vm = vm;
172
173
		vm.onShowTrigger = ko.observable(false);
174
		vm.onHideTrigger = ko.observable(false);
175
176
		vm.viewModelName = ViewModelClass.__name;
177
		vm.viewModelNames = ViewModelClass.__names;
178
		vm.viewModelTemplateID = ViewModelClass.__templateID;
179
		vm.viewModelPosition = ViewModelClass.__type;
180
181
		if (vmPlace && 1 === vmPlace.length)
182
		{
183
			vmDom = $('<div></div>').addClass('rl-view-model').addClass('RL-' + vm.viewModelTemplateID).hide();
184
			vmDom.appendTo(vmPlace);
185
186
			vm.viewModelDom = vmDom;
187
			ViewModelClass.__dom = vmDom;
188
189
			if (ViewType.Popup === position)
190
			{
191
				vm.cancelCommand = vm.closeCommand = createCommand(() => {
192
					hideScreenPopup(ViewModelClass);
193
				});
194
195
				vm.modalVisibility.subscribe((value) => {
196
					if (value)
197
					{
198
						vm.viewModelDom.show();
199
						vm.storeAndSetKeyScope();
200
201
						popupVisibilityNames.push(vm.viewModelName);
202
						vm.viewModelDom.css('z-index', 3000 + popupVisibilityNames().length + 10);
203
204
						if (vm.onShowTrigger)
205
						{
206
							vm.onShowTrigger(!vm.onShowTrigger());
207
						}
208
209
						delegateRun(vm, 'onShowWithDelay', [], 500);
210
					}
211
					else
212
					{
213
						delegateRun(vm, 'onHide');
214
						delegateRun(vm, 'onHideWithDelay', [], 500);
215
216
						if (vm.onHideTrigger)
217
						{
218
							vm.onHideTrigger(!vm.onHideTrigger());
219
						}
220
221
						vm.restoreKeyScope();
222
223
						vmRunHook('view-model-on-hide', ViewModelClass);
224
225
						popupVisibilityNames.remove(vm.viewModelName);
226
						vm.viewModelDom.css('z-index', 2000);
227
228
						_.delay(() => vm.viewModelDom.hide(), 300);
229
					}
230
				});
231
			}
232
233
			vmRunHook('view-model-pre-build', ViewModelClass, vmDom);
234
235
			ko.applyBindingAccessorsToNode(vmDom[0], {
236
				translatorInit: true,
237
				template: () => ({name: vm.viewModelTemplateID})
238
			}, vm);
239
240
			delegateRun(vm, 'onBuild', [vmDom]);
241
			if (vm && ViewType.Popup === position)
242
			{
243
				vm.registerPopupKeyDown();
244
			}
245
246
			vmRunHook('view-model-post-build', ViewModelClass, vmDom);
247
		}
248
		else
249
		{
250
			log('Cannot find view model position: ' + position);
251
		}
252
	}
253
254
	return ViewModelClass ? ViewModelClass.__vm : null;
255
}
256
257
/**
258
 * @param {Function} ViewModelClassToShow
259
 * @param {Array=} params
260
 * @returns {void}
261
 */
262
export function showScreenPopup(ViewModelClassToShow, params = [])
263
{
264
	const ModalView = getScreenPopup(ViewModelClassToShow);
265
	if (ModalView)
266
	{
267
		buildViewModel(ModalView);
268
269
		if (ModalView.__vm && ModalView.__dom)
270
		{
271
			delegateRun(ModalView.__vm, 'onBeforeShow', params || []);
272
273
			ModalView.__vm.modalVisibility(true);
274
275
			delegateRun(ModalView.__vm, 'onShow', params || []);
276
277
			vmRunHook('view-model-on-show', ModalView, params || []);
278
		}
279
	}
280
}
281
282
/**
283
 * @param {Function} ViewModelClassToShow
284
 * @returns {void}
285
 */
286
export function warmUpScreenPopup(ViewModelClassToShow)
287
{
288
	const ModalView = getScreenPopup(ViewModelClassToShow);
289
	if (ModalView)
290
	{
291
		buildViewModel(ModalView);
292
293
		if (ModalView.__vm && ModalView.__dom)
294
		{
295
			delegateRun(ModalView.__vm, 'onWarmUp');
296
		}
297
	}
298
}
299
300
/**
301
 * @param {Function} ViewModelClassToShow
302
 * @returns {boolean}
303
 */
304
export function isPopupVisible(ViewModelClassToShow)
305
{
306
	const ModalView = getScreenPopup(ViewModelClassToShow);
307
	return ModalView && ModalView.__vm ? ModalView.__vm.modalVisibility() : false;
308
}
309
310
/**
311
 * @param {string} screenName
312
 * @param {string} subPart
313
 * @returns {void}
314
 */
315
export function screenOnRoute(screenName, subPart)
316
{
317
	let
318
		vmScreen = null,
319
		isSameScreen = false,
320
		cross = null;
321
322
	if ('' === pString(screenName))
323
	{
324
		screenName = defaultScreenName;
325
	}
326
327
	if ('' !== screenName)
328
	{
329
		vmScreen = screen(screenName);
330
		if (!vmScreen)
331
		{
332
			vmScreen = screen(defaultScreenName);
333
			if (vmScreen)
334
			{
335
				subPart = screenName + '/' + subPart;
336
				screenName = defaultScreenName;
337
			}
338
		}
339
340
		if (vmScreen && vmScreen.__started)
341
		{
342
			isSameScreen = currentScreen && vmScreen === currentScreen;
343
344
			if (!vmScreen.__builded)
345
			{
346
				vmScreen.__builded = true;
347
348
				if (isNonEmptyArray(vmScreen.viewModels()))
349
				{
350
					_.each(vmScreen.viewModels(), (ViewModelClass) => {
351
						buildViewModel(ViewModelClass, vmScreen);
352
					});
353
				}
354
355
				delegateRun(vmScreen, 'onBuild');
356
			}
357
358
			_.defer(() => {
359
				// hide screen
360
				if (currentScreen && !isSameScreen)
361
				{
362
					delegateRun(currentScreen, 'onHide');
363
					delegateRun(currentScreen, 'onHideWithDelay', [], 500);
364
365
					if (currentScreen.onHideTrigger)
366
					{
367
						currentScreen.onHideTrigger(!currentScreen.onHideTrigger());
368
					}
369
370
					if (isNonEmptyArray(currentScreen.viewModels()))
371
					{
372
						_.each(currentScreen.viewModels(), (ViewModelClass) => {
373
							if (ViewModelClass.__vm && ViewModelClass.__dom && ViewType.Popup !== ViewModelClass.__vm.viewModelPosition)
374
							{
375
								ViewModelClass.__dom.hide();
376
								ViewModelClass.__vm.viewModelVisibility(false);
377
378
								delegateRun(ViewModelClass.__vm, 'onHide');
379
								delegateRun(ViewModelClass.__vm, 'onHideWithDelay', [], 500);
380
381
								if (ViewModelClass.__vm.onHideTrigger)
382
								{
383
									ViewModelClass.__vm.onHideTrigger(!ViewModelClass.__vm.onHideTrigger());
384
								}
385
							}
386
						});
387
					}
388
				}
389
				// --
390
391
				currentScreen = vmScreen;
392
393
				// show screen
394
				if (currentScreen && !isSameScreen)
395
				{
396
					delegateRun(currentScreen, 'onShow');
397
					if (currentScreen.onShowTrigger)
398
					{
399
						currentScreen.onShowTrigger(!currentScreen.onShowTrigger());
400
					}
401
402
					runHook('screen-on-show', [currentScreen.screenName(), currentScreen]);
403
404
					if (isNonEmptyArray(currentScreen.viewModels()))
405
					{
406
						_.each(currentScreen.viewModels(), (ViewModelClass) => {
407
408
							if (ViewModelClass.__vm && ViewModelClass.__dom && ViewType.Popup !== ViewModelClass.__vm.viewModelPosition)
409
							{
410
								delegateRun(ViewModelClass.__vm, 'onBeforeShow');
411
412
								ViewModelClass.__dom.show();
413
								ViewModelClass.__vm.viewModelVisibility(true);
414
415
								delegateRun(ViewModelClass.__vm, 'onShow');
416
								if (ViewModelClass.__vm.onShowTrigger)
417
								{
418
									ViewModelClass.__vm.onShowTrigger(!ViewModelClass.__vm.onShowTrigger());
419
								}
420
421
								delegateRun(ViewModelClass.__vm, 'onShowWithDelay', [], 200);
422
								vmRunHook('view-model-on-show', ViewModelClass);
423
							}
424
425
						});
426
					}
427
				}
428
				// --
429
430
				cross = vmScreen && vmScreen.__cross ? vmScreen.__cross() : null;
431
				if (cross)
432
				{
433
					cross.parse(subPart);
434
				}
435
			});
436
		}
437
	}
438
}
439
440
/**
441
 * @param {Array} screensClasses
442
 * @returns {void}
443
 */
444
export function startScreens(screensClasses)
445
{
446
	_.each(screensClasses, (CScreen) => {
447
		if (CScreen)
448
		{
449
			const
450
				vmScreen = new CScreen(),
451
				screenName = vmScreen ? vmScreen.screenName() : '';
452
453
			if (vmScreen && '' !== screenName)
454
			{
455
				if ('' === defaultScreenName)
456
				{
457
					defaultScreenName = screenName;
458
				}
459
460
				SCREENS[screenName] = vmScreen;
461
			}
462
		}
463
	});
464
465
	_.each(SCREENS, (vmScreen) => {
466
		if (vmScreen && !vmScreen.__started && vmScreen.__start)
467
		{
468
			vmScreen.__started = true;
469
			vmScreen.__start();
470
471
			runHook('screen-pre-start', [vmScreen.screenName(), vmScreen]);
472
			delegateRun(vmScreen, 'onStart');
473
			runHook('screen-post-start', [vmScreen.screenName(), vmScreen]);
474
		}
475
	});
476
477
	const cross = crossroads.create();
478
	cross.addRoute(/^([a-zA-Z0-9\-]*)\/?(.*)$/, screenOnRoute);
479
480
	hasher.initialized.add(cross.parse, cross);
481
	hasher.changed.add(cross.parse, cross);
482
	hasher.init();
483
484
	_.delay(() => $html.removeClass('rl-started-trigger').addClass('rl-started'), 100);
485
	_.delay(() => $html.addClass('rl-started-delay'), 200);
486
}
487
488
/**
489
 * @param {string} sHash
0 ignored issues
show
Documentation introduced by
The parameter sHash does not exist. Did you maybe forget to remove this comment?
Loading history...
490
 * @param {boolean=} silence = false
491
 * @param {boolean=} replace = false
492
 * @returns {void}
493
 */
494
export function setHash(hash, silence = false, replace = false)
495
{
496
	hash = '#' === hash.substr(0, 1) ? hash.substr(1) : hash;
497
	hash = '/' === hash.substr(0, 1) ? hash.substr(1) : hash;
498
499
	const cmd = replace ? 'replaceHash' : 'setHash';
500
501
	if (silence)
502
	{
503
		hasher.changed.active = false;
504
		hasher[cmd](hash);
505
		hasher.changed.active = true;
506
	}
507
	else
508
	{
509
		hasher.changed.active = true;
510
		hasher[cmd](hash);
511
		hasher.setHash(hash);
512
	}
513
}
514
515
/**
516
 * @param {Object} params
0 ignored issues
show
Documentation introduced by
The parameter params does not exist. Did you maybe forget to remove this comment?
Loading history...
517
 * @returns {Function}
518
 */
519
function viewDecorator({name, type, templateID})
520
{
521
	return (target) => {
522
		if (target)
523
		{
524
			if (name)
525
			{
526
				if (isArray(name))
527
				{
528
					target.__names = name;
529
				}
530
				else
531
				{
532
					target.__names = [name];
533
				}
534
535
				target.__name = target.__names[0];
536
			}
537
538
			if (type)
539
			{
540
				target.__type = type;
541
			}
542
543
			if (templateID)
544
			{
545
				target.__templateID = templateID;
546
			}
547
		}
548
	};
549
}
550
551
/**
552
 * @param {Object} params
0 ignored issues
show
Documentation introduced by
The parameter params does not exist. Did you maybe forget to remove this comment?
Loading history...
553
 * @returns {Function}
554
 */
555
function popupDecorator({name, templateID})
556
{
557
	return viewDecorator({name, type: ViewType.Popup, templateID});
558
}
559
560
/**
561
 * @param {Function} canExecute
562
 * @returns {Function}
563
 */
564
function commandDecorator(canExecute = true)
565
{
566
	return (target, key, descriptor) => {
567
568
		if (!key || !key.match(/Command$/))
569
		{
570
			throw new Error(`name "${key}" should end with Command suffix`);
571
		}
572
573
		const
574
			value = descriptor.value || descriptor.initializer(),
575
			normCanExecute = isFunc(canExecute) ? canExecute : () => !!canExecute;
576
577
		descriptor.value = function(...args) {
578
			if (normCanExecute.call(this, this))
579
			{
580
				value.apply(this, args);
581
			}
582
583
			return false;
584
		};
585
586
		descriptor.value.__realCanExecute = normCanExecute;
587
		descriptor.value.isCommand = true;
588
589
		return descriptor;
590
	};
591
}
592
593
export {
594
	commandDecorator, commandDecorator as command,
595
	viewDecorator, viewDecorator as view, viewDecorator as viewModel,
596
	popupDecorator, popupDecorator as popup
597
};
598